﻿using ChartManager.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ChartManager.Controllers
{
    public static class SongController
    {
        /// <summary>
        /// 增加歌曲
        /// </summary>
        /// <param name="context">EFContext</param>
        /// <param name="Songs">给定歌曲</param>
        public static void AddSongs(EFContext context, params Song[] Songs)
        {
            // 没有给定歌曲，返回
            if (Songs.Count() <= 0)
                return;
            // 遍历给定歌曲
            foreach (var song in Songs)
            {
                // 给定歌曲中互相存在重复歌曲
                if (Songs.Any(obj => obj != song && song.Name.Equals(obj.Name) && obj.Singers.SingersEquals(song.Singers)))
                    throw new ArgumentException("存在多个相同歌曲", nameof(Songs));
                // 给定歌曲中与数据库内的歌曲重复
                if (context.Songs.AsNoTracking().ToList().Any(obj => obj.Name.Equals(song.Name) && obj.Singers.SingersEquals(song.Singers)))
                    throw new ArgumentException("已存在相同歌曲", nameof(Songs));
            }
            context.Songs.AddRange(Songs);
            context.SaveChanges();
        }

        /// <summary>
        /// 获取所有歌曲
        /// </summary>
        /// <param name="context">EFContext</param>
        /// <param name="getLastWeek">是否仅获取上周歌曲，默认为true</param>
        /// <returns>歌曲</returns>
        public static IEnumerable<Song> GetSongs(EFContext context, bool getLastWeek = true)
        {
            if (getLastWeek)
            {
                try
                {
                    // 选择上周榜单行中的歌曲返回
                    return ChartController.GetLastWeek(context).Rows.Select(row => row.Song);
                }
                // 没有上周榜单时返回null
                catch (IndexOutOfRangeException)
                {
                    return null;
                }
            }
            else return context.Songs;
        }

        /// <summary>
        /// 获取单首歌曲
        /// </summary>
        /// <param name="context">EFContext</param>
        /// <param name="Singer">歌手</param>
        /// <param name="Name">歌名</param>
        /// <returns>歌曲</returns>
        public static Song GetSingleSong(EFContext context, string Singer, string Name)
        {
            // 只要同名歌曲的歌手名单中包含该歌手，就返回
            try
            {
                return (from song in context.Songs.ToList()
                        where song.Name.Equals(Name) && song.Singers.Contains(Singer)
                        select song).Single();
            }
            // 如果查询到的歌曲存在多个，则抛出异常
            catch (Exception ex)
            {
                throw new Exception("未查询到正确歌曲", ex);
            }
        }

        /// <summary>
        /// 获取单首歌曲
        /// </summary>
        /// <param name="context">EFContext</param>
        /// <param name="Singers">歌手名单</param>
        /// <param name="Name">歌名</param>
        /// <returns>歌曲</returns>
        public static Song GetSingleSong(EFContext context, List<string> Singers, string Name)
        {
            // 只要同名歌曲的歌手名单中与给定名单相同（不按顺序），就返回
            try
            {
                return (from song in context.Songs.ToList()
                        where song.Name.Equals(Name) && song.Singers.SingersEquals(Singers)
                        select song).SingleOrDefault();
            }
            // 如果查询到的歌曲存在多个，则抛出异常
            catch (Exception ex)
            {
                throw new Exception("未查询到正确歌曲", ex);
            }
        }

        /// <summary>
        /// 获取新增歌曲
        /// </summary>
        /// <param name="context">EFContext</param>
        /// <returns>歌曲</returns>
        public static IEnumerable<Song> GetNewSongs(EFContext context)
        {
            // 以当前拥有的歌曲与已经上榜的歌曲相比，返回没有上榜的歌曲，即为新增歌曲
            return GetExceptSongs(context.Songs.ToList(), context.Rows.Select(row => row.Song));
        }

        /// <summary>
        /// 获取两个歌曲集合的差集
        /// </summary>
        /// <param name="set1">集合1</param>
        /// <param name="set2">集合2</param>
        /// <returns>差集</returns>
        public static IEnumerable<Song> GetExceptSongs(IEnumerable<Song> set1, IEnumerable<Song> set2)
        {
            var result = new List<Song>();
            // 遍历集合1
            foreach (var song in set1)
            {
                // 本次循环歌曲中没有出现在集合2中时，加入到返回结果中
                if (!set2.Any(obj => obj.Name.Equals(song.Name) && obj.Singers.SingersEquals(song.Singers)))
                    result.Add(song);
            }
            return result;
        }

        /// <summary>
        /// 扩展转换形如{0} - {1}的字符串为歌曲
        /// </summary>
        /// <param name="sz">字符串</param>
        /// <returns>歌曲</returns>
        public static Song toSong(this string sz)
        {
            // 如果输入框中没有歌曲，返回null
            if (string.IsNullOrWhiteSpace(sz))
                throw new ArgumentNullException("给定字符串为空", nameof(sz));
            var split = sz.Split(new string[] { " - " }, StringSplitOptions.None);
            // 检查分裂结果是否只有两个元素
            if (split.Count() != 2)
                throw new ArgumentException("给定字符串格式错误", nameof(sz));
            return new Song() { SingersAsString = split[0], Name = split[1] };
        }

        /// <summary>
        /// 扩展对比两个歌手名单集合
        /// </summary>
        /// <param name="list1">集合1</param>
        /// <param name="list2">集合2</param>
        /// <returns>对比结果</returns>
        public static bool SingersEquals(this IEnumerable<string> list1, IEnumerable<string> list2)
        {
            if (list1 == null && list2 == null) return true;
            if (list1 == null || list2 == null) return false;
            if (list1.Count() != list2.Count()) return false;
            foreach (var item in list1)
            {
                // 只要不包含就返回false，不检查双方下标
                if (!list2.Contains(item)) return false;
            }
            return true;
        }

        /// <summary>
        /// 获取歌曲信息
        /// </summary>
        /// <param name="context"></param>
        /// <param name="song"></param>
        /// <returns></returns>
        public static SongInfo GetSongInfo(EFContext context, Song song)
        {
            // 先假定该歌曲上过榜，并查出该歌曲的记录
            var result = new SongInfo(true)
            {
                Records = (from row in context.Rows.ToList()
                           where row.Song.Name.Equals(song.Name) && row.Song.Singers.SingersEquals(song.Singers)
                           select row).ToList()
            };
            // 如果没有上榜记录，返回新对象
            if (result.Records.Count < 1)
                return new SongInfo(false);
            // 否则返回假定对象
            return result;
        }

        public class SongInfo
        {
            /// <summary>
            /// 是否曾经上过榜
            /// </summary>
            public bool WasOnChart { get; private set; }
            /// <summary>
            /// 上榜成绩
            /// </summary>
            public List<ChartRow> Records { get; set; }

            public SongInfo(bool wasOnChart)
            {
                WasOnChart = wasOnChart;
                // 没有上过榜时，记录为null
                Records = wasOnChart ? new List<ChartRow>() : null;
            }
        }
    }
}
